Ismerje meg a JavaScript Promise kombinátorokat (Promise.all, Promise.allSettled, Promise.race, Promise.any) a hatékony és robusztus aszinkron programozáshoz globális alkalmazásokban.
JavaScript Promise Kombinátorok: Fejlett Aszinkron Minták Globális Alkalmazásokhoz
Az aszinkron programozás a modern JavaScript egyik sarokköve, kĂĽlönösen olyan webalkalmazások kĂ©szĂtĂ©sekor, amelyek API-kkal, adatbázisokkal kommunikálnak, vagy idĹ‘igĂ©nyes műveleteket vĂ©geznek. A JavaScript Promise-ok hatĂ©kony absztrakciĂłt nyĂşjtanak az aszinkron műveletek kezelĂ©sĂ©re, de a mesteri szintű használatukhoz szĂĽksĂ©ges a haladĂł minták megĂ©rtĂ©se. Ez a cikk a JavaScript Promise kombinátorokat – Promise.all, Promise.allSettled, Promise.race Ă©s Promise.any – vizsgálja meg, Ă©s azt, hogy hogyan használhatĂłk hatĂ©kony Ă©s robusztus aszinkron munkafolyamatok lĂ©trehozására, kĂĽlönösen a változĂł hálĂłzati feltĂ©telekkel Ă©s adatforrásokkal rendelkezĹ‘ globális alkalmazások kontextusában.
A Promise-ok megértése: Gyors áttekintés
Mielőtt belevágnánk a kombinátorokba, tekintsük át gyorsan a Promise-okat. A Promise egy aszinkron művelet végső eredményét képviseli. Három állapotban lehet:
- Pending (FĂĽggĹ‘ben): A kezdeti állapot, sem nem teljesĂĽlt, sem nem elutasĂtott.
- Fulfilled (Teljesült): A művelet sikeresen befejeződött, egy eredményértékkel.
- Rejected (ElutasĂtott): A művelet sikertelen volt, egy indokkal (általában egy Error objektummal).
A Promise-ok tisztább Ă©s kezelhetĹ‘bb mĂłdot kĂnálnak az aszinkron műveletek kezelĂ©sĂ©re a hagyományos visszahĂvásokhoz (callbackekhez) kĂ©pest. JavĂtják a kĂłd olvashatĂłságát Ă©s egyszerűsĂtik a hibakezelĂ©st. KulcsfontosságĂş, hogy ezek kĂ©pezik az alapját a Promise kombinátoroknak, amelyeket most megvizsgálunk.
Promise Kombinátorok: Aszinkron Műveletek Összehangolása
A Promise kombinátorok a Promise objektum statikus metĂłdusai, amelyek lehetĹ‘vĂ© teszik több Promise kezelĂ©sĂ©t Ă©s koordinálását. ErĹ‘teljes eszközöket nyĂşjtanak összetett aszinkron munkafolyamatok Ă©pĂtĂ©sĂ©hez. Vizsgáljuk meg mindegyiket rĂ©szletesen.
Promise.all(): Promise-ok párhuzamos vĂ©grehajtása Ă©s eredmĂ©nyek összesĂtĂ©se
A Promise.all() egy iterálhatĂł (általában egy tömbnyi) Promise-t fogad bemenetkĂ©nt, Ă©s egyetlen Promise-t ad vissza. Ez a visszaadott Promise akkor teljesĂĽl, amikor az összes bemeneti Promise teljesĂĽlt. Ha bármelyik bemeneti Promise elutasĂtásra kerĂĽl, a visszaadott Promise azonnal elutasĂtásra kerĂĽl az elsĹ‘ elutasĂtott Promise indokával.
Felhasználási eset: Amikor egyszerre több API-bĂłl kell adatokat lekĂ©rnie Ă©s a kombinált eredmĂ©nyeket feldolgoznia, a Promise.all() ideális. KĂ©pzeljen el pĂ©ldául egy műszerfalat, amely a világ kĂĽlönbözĹ‘ városainak idĹ‘járási adatait jelenĂti meg. Az egyes városok adatai kĂĽlön API hĂvással kĂ©rhetĹ‘k le.
async function fetchWeatherData(city) {
try {
const response = await fetch(`https://api.example.com/weather?city=${city}`); // Replace with a real API endpoint
if (!response.ok) {
throw new Error(`Failed to fetch weather data for ${city}`);
}
return await response.json();
} catch (error) {
console.error(`Error fetching weather data for ${city}: ${error}`);
throw error; // Re-throw the error to be caught by Promise.all
}
}
async function displayWeatherData() {
const cities = ['London', 'Tokyo', 'New York', 'Sydney'];
try {
const weatherDataPromises = cities.map(city => fetchWeatherData(city));
const weatherData = await Promise.all(weatherDataPromises);
weatherData.forEach((data, index) => {
console.log(`Weather in ${cities[index]}:`, data);
// Update the UI with the weather data
});
} catch (error) {
console.error('Failed to fetch weather data for all cities:', error);
// Display an error message to the user
}
}
displayWeatherData();
Megfontolások globális alkalmazásokhoz:
- Hálózati késleltetés: A különböző földrajzi helyeken lévő különböző API-khoz intézett kérések eltérő késleltetéssel rendelkezhetnek. A
Promise.all()nem garantálja a Promise-ok teljesĂĽlĂ©sĂ©nek sorrendjĂ©t, csak azt, hogy mindegyik teljesĂĽl (vagy egy elutasĂtásra kerĂĽl), mielĹ‘tt a kombinált Promise lezárul. - API ráta korlátozás: Ha több kĂ©rĂ©st intĂ©z ugyanahhoz az API-hoz, vagy több, közös ráta korlátozással rendelkezĹ‘ API-hoz, tĂşllĂ©pheti ezeket a korlátokat. Alkalmazzon olyan stratĂ©giákat, mint a kĂ©rĂ©sek sorba állĂtása vagy az exponenciális visszalĂ©pĂ©s (exponential backoff) a ráta korlátozás elegáns kezelĂ©sĂ©re.
- HibakezelĂ©s: Ne feledje, hogy ha bármelyik Promise elutasĂtásra kerĂĽl, az egĂ©sz
Promise.all()művelet sikertelen lesz. Ez nem mindig kĂvánatos, ha rĂ©szleges adatokat szeretne megjelenĂteni akkor is, ha nĂ©hány kĂ©rĂ©s meghiĂşsul. Ilyen esetekben fontolja meg aPromise.allSettled()használatát (amelyet alább ismertetĂĽnk).
Promise.allSettled(): A siker és a kudarc egyedi kezelése
A Promise.allSettled() hasonlĂł a Promise.all()-hoz, de egy lĂ©nyeges kĂĽlönbsĂ©ggel: megvárja, amĂg minden bemeneti Promise lezárul (settle), fĂĽggetlenĂĽl attĂłl, hogy teljesĂĽlnek vagy elutasĂtásra kerĂĽlnek. A visszaadott Promise mindig egy objektumokbĂłl állĂł tömbbel teljesĂĽl, amelyek mindegyike leĂrja a megfelelĹ‘ bemeneti Promise kimenetelĂ©t. Minden objektumnak van egy status tulajdonsága (ami "fulfilled" vagy "rejected") Ă©s egy value (ha teljesĂĽlt) vagy reason (ha elutasĂtották) tulajdonsága.
Felhasználási eset: Amikor több aszinkron művelet eredmĂ©nyĂ©t kell összegyűjtenie, Ă©s elfogadhatĂł, hogy nĂ©melyikĂĽk meghiĂşsul anĂ©lkĂĽl, hogy az egĂ©sz műveletet meghiĂşsĂtaná, a Promise.allSettled() a jobb választás. KĂ©pzeljen el egy rendszert, amely több fizetĂ©si átjárĂłn keresztĂĽl dolgoz fel fizetĂ©seket. Lehet, hogy meg akarja prĂłbálni az összes fizetĂ©st, Ă©s rögzĂteni szeretnĂ©, hogy melyek sikerĂĽltek Ă©s melyek hiĂşsultak meg.
async function processPayment(paymentGateway, amount) {
try {
const response = await paymentGateway.process(amount); // Replace with a real payment gateway integration
if (response.status === 'success') {
return { status: 'fulfilled', value: `Payment processed successfully via ${paymentGateway.name}` };
} else {
throw new Error(`Payment failed via ${paymentGateway.name}: ${response.message}`);
}
} catch (error) {
return { status: 'rejected', reason: `Payment failed via ${paymentGateway.name}: ${error.message}` };
}
}
async function processMultiplePayments(paymentGateways, amount) {
const paymentPromises = paymentGateways.map(gateway => processPayment(gateway, amount));
const results = await Promise.allSettled(paymentPromises);
results.forEach((result, index) => {
if (result.status === 'fulfilled') {
console.log(result.value);
} else {
console.error(result.reason);
}
});
// Analyze the results to determine overall success/failure
const successfulPayments = results.filter(result => result.status === 'fulfilled').length;
const failedPayments = results.filter(result => result.status === 'rejected').length;
console.log(`Successful payments: ${successfulPayments}`);
console.log(`Failed payments: ${failedPayments}`);
}
// Example payment gateways
const paymentGateways = [
{ name: 'PayPal', process: (amount) => Promise.resolve({ status: 'success', message: 'Payment successful' }) },
{ name: 'Stripe', process: (amount) => Promise.reject({ status: 'error', message: 'Insufficient funds' }) },
{ name: 'Worldpay', process: (amount) => Promise.resolve({ status: 'success', message: 'Payment successful' }) },
];
processMultiplePayments(paymentGateways, 100);
Megfontolások globális alkalmazásokhoz:
- Robusztusság: A
Promise.allSettled()növeli az alkalmazások robusztusságát azáltal, hogy biztosĂtja, hogy minden aszinkron művelet megkĂsĂ©rlĂ©sre kerĂĽl, mĂ©g ha nĂ©melyikĂĽk meg is hiĂşsul. Ez kĂĽlönösen fontos az elosztott rendszerekben, ahol a hibák gyakoriak. - RĂ©szletes jelentĂ©skĂ©szĂtĂ©s: Az eredmĂ©nytömb rĂ©szletes informáciĂłkat nyĂşjt minden egyes művelet kimenetelĂ©rĹ‘l, lehetĹ‘vĂ© tĂ©ve a hibák naplĂłzását, a sikertelen műveletek ĂşjraprĂłbálását, vagy a felhasználĂłknak nyĂşjtott specifikus visszajelzĂ©st.
- RĂ©szleges siker: Könnyen meghatározhatja az általános sikeressĂ©gi arányt, Ă©s megfelelĹ‘ intĂ©zkedĂ©seket tehet a sikeres Ă©s sikertelen műveletek száma alapján. PĂ©ldául alternatĂv fizetĂ©si mĂłdokat kĂnálhat, ha az elsĹ‘dleges átjárĂł meghiĂşsul.
Promise.race(): A leggyorsabb eredmény kiválasztása
A Promise.race() szintĂ©n egy iterálhatĂł Promise-t fogad bemenetkĂ©nt Ă©s egyetlen Promise-t ad vissza. Azonban, ellentĂ©tben a Promise.all()-lal Ă©s a Promise.allSettled()-del, a Promise.race() lezárul, amint bármelyik bemeneti Promise lezárul (akár teljesĂĽl, akár elutasĂtásra kerĂĽl). A visszaadott Promise az elsĹ‘ lezárult Promise Ă©rtĂ©kĂ©vel vagy indokával teljesĂĽl vagy utasĂtĂłdik el.
Felhasználási eset: Amikor a leggyorsabb választ kell kiválasztania több forrás közĂĽl, a Promise.race() jĂł választás. KĂ©pzelje el, hogy több szervert kĂ©rdez le ugyanazokĂ©rt az adatokĂ©rt, Ă©s az elsĹ‘kĂ©nt kapott választ használja. Ez javĂthatja a teljesĂtmĂ©nyt Ă©s a válaszkĂ©szsĂ©get, kĂĽlönösen olyan helyzetekben, amikor egyes szerverek ideiglenesen nem elĂ©rhetĹ‘k vagy lassabbak a többinĂ©l.
async function fetchDataFromServer(serverURL) {
try {
const response = await fetch(serverURL, {signal: AbortSignal.timeout(5000)}); //Add a timeout of 5 seconds
if (!response.ok) {
throw new Error(`Failed to fetch data from ${serverURL}`);
}
return await response.json();
} catch (error) {
console.error(`Error fetching data from ${serverURL}: ${error}`);
throw error;
}
}
async function getFastestResponse() {
const serverURLs = [
'https://server1.example.com/data', // Replace with real server URLs
'https://server2.example.com/data',
'https://server3.example.com/data',
];
try {
const dataPromises = serverURLs.map(serverURL => fetchDataFromServer(serverURL));
const fastestData = await Promise.race(dataPromises);
console.log('Fastest data received:', fastestData);
// Use the fastest data
} catch (error) {
console.error('Failed to get data from any server:', error);
// Handle the error
}
}
getFastestResponse();
Megfontolások globális alkalmazásokhoz:
- Időtúllépések: Kulcsfontosságú az időtúllépések implementálása a
Promise.race()használatakor, hogy megakadályozzuk a visszaadott Promise vĂ©gtelen várakozását, ha nĂ©hány bemeneti Promise soha nem zárul le. A fenti pĂ©lda az `AbortSignal.timeout()`-ot használja ennek elĂ©rĂ©sĂ©re. - HálĂłzati körĂĽlmĂ©nyek: A leggyorsabb szerver a felhasználĂł földrajzi helyĂ©tĹ‘l Ă©s hálĂłzati körĂĽlmĂ©nyeitĹ‘l fĂĽggĹ‘en változhat. Fontolja meg egy TartalomkĂ©zbesĂtĹ‘ HálĂłzat (CDN) használatát a tartalom terjesztĂ©sĂ©re Ă©s a teljesĂtmĂ©ny javĂtására a világ minden táján Ă©lĹ‘ felhasználĂłk számára.
- HibakezelĂ©s: Ha az a Promise, amelyik 'megnyeri' a versenyt, elutasĂtásra kerĂĽl, akkor az egĂ©sz Promise.race elutasĂtásra kerĂĽl. BiztosĂtsa, hogy minden Promise megfelelĹ‘ hibakezelĂ©ssel rendelkezzen a váratlan elutasĂtások megelĹ‘zĂ©se Ă©rdekĂ©ben. Továbbá, ha a "gyĹ‘ztes" promise egy idĹ‘tĂşllĂ©pĂ©s miatt utasĂtĂłdik el (ahogy a fenti pĂ©ldában láthatĂł), a többi promise a háttĂ©rben tovább fut. Lehet, hogy logikát kell hozzáadnia a többi promise megszakĂtásához az `AbortController` segĂtsĂ©gĂ©vel, ha már nincs rájuk szĂĽksĂ©g.
Promise.any(): Az első teljesülés elfogadása
A Promise.any() hasonlĂł a Promise.race()-hez, de kissĂ© eltĂ©rĹ‘ viselkedĂ©ssel. Megvárja az elsĹ‘ teljesĂĽlĹ‘ bemeneti Promise-t. Ha az összes bemeneti Promise elutasĂtásra kerĂĽl, a Promise.any() egy AggregateError-ral utasĂtĂłdik el, amely tartalmazza az elutasĂtási indokok tömbjĂ©t.
Felhasználási eset: Amikor több forrásbĂłl kell adatokat lekĂ©rnie, Ă©s csak az elsĹ‘ sikeres eredmĂ©ny Ă©rdekli, a Promise.any() jĂł választás. Ez hasznos, ha redundáns adatforrásai vagy alternatĂv API-jai vannak, amelyek ugyanazt az informáciĂłt szolgáltatják. A sikert helyezi elĹ‘tĂ©rbe a sebessĂ©ggel szemben, mivel megvárja az elsĹ‘ teljesĂĽlĂ©st, mĂ©g akkor is, ha nĂ©hány Promise gyorsan elutasĂtásra kerĂĽl.
async function fetchDataFromSource(sourceURL) {
try {
const response = await fetch(sourceURL);
if (!response.ok) {
throw new Error(`Failed to fetch data from ${sourceURL}`);
}
return await response.json();
} catch (error) {
console.error(`Error fetching data from ${sourceURL}: ${error}`);
throw error;
}
}
async function getFirstSuccessfulData() {
const dataSources = [
'https://source1.example.com/data', // Replace with real data source URLs
'https://source2.example.com/data',
'https://source3.example.com/data',
];
try {
const dataPromises = dataSources.map(sourceURL => fetchDataFromSource(sourceURL));
const data = await Promise.any(dataPromises);
console.log('First successful data received:', data);
// Use the successful data
} catch (error) {
if (error instanceof AggregateError) {
console.error('Failed to get data from any source:', error.errors);
// Handle the error
} else {
console.error('An unexpected error occurred:', error);
}
}
}
getFirstSuccessfulData();
Megfontolások globális alkalmazásokhoz:
- Redundancia: A
Promise.any()különösen hasznos, ha olyan redundáns adatforrásokkal dolgozik, amelyek hasonló információkat szolgáltatnak. Ha egy forrás nem elérhető vagy lassú, támaszkodhat a többire az adatok szolgáltatásához. - Hibakezelés: Ügyeljen arra, hogy kezelje az
AggregateError-t, amely akkor dobĂłdik, ha az összes bemeneti Promise elutasĂtásra kerĂĽl. Ez a hiba tartalmazza az egyedi elutasĂtási indokok tömbjĂ©t, lehetĹ‘vĂ© tĂ©ve a problĂ©mák hibakeresĂ©sĂ©t Ă©s diagnosztizálását. - PrioritáskezelĂ©s: A Promise-ok
Promise.any()-nek valĂł átadásának sorrendje számĂt. Helyezze a legmegbĂzhatĂłbb vagy leggyorsabb adatforrásokat az elsĹ‘ helyre, hogy növelje a sikeres eredmĂ©ny valĂłszĂnűsĂ©gĂ©t.
A megfelelő kombinátor kiválasztása: Összefoglaló
Itt egy gyors összefoglalĂł, amely segĂt kiválasztani a megfelelĹ‘ Promise kombinátort az Ă–n igĂ©nyeinek megfelelĹ‘en:
- Promise.all(): Akkor használja, ha azt szeretnĂ©, hogy minden Promise sikeresen teljesĂĽljön, Ă©s azonnal hibát szeretne kapni, ha bármelyik Promise elutasĂtásra kerĂĽl.
- Promise.allSettled(): Akkor használja, ha meg akarja várni az összes Promise lezárulását, függetlenül a sikertől vagy a kudarctól, és részletes információra van szüksége minden egyes kimenetelről.
- Promise.race(): Akkor használja, ha a leggyorsabb eredményt szeretné kiválasztani több Promise közül, és csak az elsőként lezárulóval törődik.
- Promise.any(): Akkor használja, ha az elsĹ‘ sikeres eredmĂ©nyt szeretnĂ© elfogadni több Promise közĂĽl, Ă©s nem bánja, ha nĂ©hány Promise elutasĂtásra kerĂĽl.
Haladó minták és legjobb gyakorlatok
A Promise kombinátorok alapvető használatán túlmenően számos haladó mintát és legjobb gyakorlatot érdemes szem előtt tartani:
Párhuzamosság korlátozása
Nagy számú Promise kezelésekor az összes párhuzamos végrehajtása túlterhelheti a rendszert vagy túllépheti az API ráta korlátokat. A párhuzamosságot korlátozhatja olyan technikákkal, mint:
- Darabolás (Chunking): Ossza a Promise-okat kisebb darabokra, és dolgozza fel az egyes darabokat egymás után.
- Szemafor használata: Implementáljon egy szemafor mechanizmust az egyidejű műveletek számának szabályozására.
Íme egy példa a darabolás használatára:
async function processInChunks(promises, chunkSize) {
const results = [];
for (let i = 0; i < promises.length; i += chunkSize) {
const chunk = promises.slice(i, i + chunkSize);
const chunkResults = await Promise.all(chunk);
results.push(...chunkResults);
}
return results;
}
// Example usage
const myPromises = [...Array(100)].map((_, i) => Promise.resolve(i)); //Create 100 promises
processInChunks(myPromises, 10) // Process 10 promises at a time
.then(results => console.log('All promises resolved:', results));
Hibakezelés elegánsan
A megfelelő hibakezelés kulcsfontosságú a Promise-okkal való munka során. Használjon try...catch blokkokat az aszinkron műveletek során esetlegesen előforduló hibák elkapására. Fontolja meg olyan könyvtárak használatát, mint a p-retry vagy a retry a sikertelen műveletek automatikus újrapróbálására.
async function fetchDataWithRetry(url, retries = 3) {
try {
const response = await fetch(url);
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (retries > 0) {
console.log(`Retrying in 1 second... (Retries left: ${retries})`);
await new Promise(resolve => setTimeout(resolve, 1000)); // Wait 1 second
return fetchDataWithRetry(url, retries - 1);
} else {
console.error('Max retries reached. Operation failed.');
throw error;
}
}
}
Az Async/Await használata
Az async Ă©s await egy szinkronabbnak tűnĹ‘ mĂłdot biztosĂt a Promise-okkal valĂł munkára. JelentĹ‘sen javĂthatják a kĂłd olvashatĂłságát Ă©s karbantarthatĂłságát.
Ne felejtse el a try...catch blokkokat használni az await kifejezések körül a lehetséges hibák kezelésére.
MegszakĂtás
Bizonyos esetekben szĂĽksĂ©g lehet a fĂĽggĹ‘ben lĂ©vĹ‘ Promise-ok megszakĂtására, kĂĽlönösen hosszan futĂł műveletek vagy felhasználĂł által kezdemĂ©nyezett akciĂłk esetĂ©n. Az AbortController API segĂtsĂ©gĂ©vel jelezheti, hogy egy Promise-t meg kell szakĂtani.
const controller = new AbortController();
const signal = controller.signal;
async function fetchDataWithCancellation(url) {
try {
const response = await fetch(url, { signal });
if (!response.ok) {
throw new Error(`HTTP error! status: ${response.status}`);
}
return await response.json();
} catch (error) {
if (error.name === 'AbortError') {
console.log('Fetch aborted');
} else {
console.error('Error fetching data:', error);
}
throw error;
}
}
fetchDataWithCancellation('https://api.example.com/data')
.then(data => console.log('Data received:', data))
.catch(error => console.error('Fetch failed:', error));
// Cancel the fetch operation after 5 seconds
setTimeout(() => {
controller.abort();
}, 5000);
KonklĂşziĂł
A JavaScript Promise kombinátorok hatĂ©kony eszközök robusztus Ă©s hatĂ©kony aszinkron alkalmazások kĂ©szĂtĂ©sĂ©hez. A Promise.all, Promise.allSettled, Promise.race Ă©s Promise.any árnyalatainak megĂ©rtĂ©sĂ©vel összetett aszinkron munkafolyamatokat hangszerelhet, elegánsan kezelheti a hibákat Ă©s optimalizálhatja a teljesĂtmĂ©nyt. Globális alkalmazások fejlesztĂ©sekor kulcsfontosságĂş a hálĂłzati kĂ©sleltetĂ©s, az API ráta korlátok Ă©s az adatforrások megbĂzhatĂłságának figyelembevĂ©tele. Az ebben a cikkben tárgyalt minták Ă©s legjobb gyakorlatok alkalmazásával olyan JavaScript alkalmazásokat hozhat lĂ©tre, amelyek egyszerre teljesĂtmĂ©nyorientáltak Ă©s ellenállĂłak, kiválĂł felhasználĂłi Ă©lmĂ©nyt nyĂşjtva a világ minden táján Ă©lĹ‘ felhasználĂłknak.